package models; import java.security.NoSuchAlgorithmException; import java.security.spec.InvalidKeySpecException; import java.sql.Timestamp; import java.util.ArrayList; import java.util.Date; import java.util.LinkedHashMap; import java.util.List; import java.util.Map; import javax.persistence.CascadeType; import javax.persistence.Column; import javax.persistence.Entity; import javax.persistence.JoinColumn; import javax.persistence.JoinTable; import javax.persistence.ManyToMany; import javax.persistence.ManyToOne; import javax.persistence.OneToMany; import javax.persistence.OrderBy; import javax.persistence.Table; import javax.persistence.Transient; import org.apache.commons.collections.CollectionUtils; import org.apache.commons.lang3.StringUtils; import org.joda.time.DateTime; import org.joda.time.Period; import org.joda.time.format.PeriodFormatter; import org.joda.time.format.PeriodFormatterBuilder; import play.Logger; import play.data.format.Formats; import play.data.validation.Constraints; import play.db.ebean.Model; import uk.bl.Const; import uk.bl.api.PasswordHash; import uk.bl.api.models.FieldModel; import uk.bl.exception.ActException; import com.avaje.ebean.Expr; import com.avaje.ebean.ExpressionList; import com.avaje.ebean.Page; import com.fasterxml.jackson.annotation.JsonIgnore; import com.fasterxml.jackson.annotation.JsonProperty; import controllers.ApplicationController; /** * User entity managed by Ebean */ @Entity @Table(name="creator") public class User extends ActModel { /** * */ private static final long serialVersionUID = -5018094620896138537L; @JsonIgnore @OneToMany(mappedBy = "authorUser", cascade = CascadeType.PERSIST) public List<Target> targets; @JsonIgnore @OneToMany(mappedBy = "user", cascade = CascadeType.ALL) public List<CrawlPermission> crawlPermissions; @JsonIgnore @ManyToOne @JoinColumn(name="organisation_id") public Organisation organisation; @JsonIgnore @OrderBy("name asc") @ManyToMany(cascade = CascadeType.ALL) @JoinTable(name = "role_user", joinColumns = { @JoinColumn(name = "user_id", referencedColumnName="id") }, inverseJoinColumns = { @JoinColumn(name = "role_id", referencedColumnName="id") }) public List<Role> roles = new ArrayList<Role>(); @Constraints.Required(message="Email is required") @Formats.NonEmpty @JsonProperty("mail") public String email; @JsonIgnore public String password; // FROM ACT @Constraints.Required public String name; @JsonIgnore public String affiliation; @JsonIgnore @JsonProperty public String edit_url; @Transient @JsonIgnore @JsonProperty public String last_access; @Transient @JsonIgnore @JsonProperty public String last_login; @Transient @JsonIgnore @JsonProperty public Long status; @Transient @JsonIgnore public String roleHolder; @Transient @JsonIgnore @JsonProperty public String language; @Transient @JsonIgnore @JsonProperty public Long feed_nid; public boolean ddhaptUser; @Column(name ="last_login") public Timestamp lastLogin; @Transient @JsonIgnore @JsonProperty private FieldModel field_affiliation; @Transient @JsonIgnore @JsonProperty private String uid; @Transient @JsonIgnore @JsonProperty private Long created; @Transient @JsonProperty("roles") @JsonIgnore private Object rolesAct; @Transient private String theme; // { // "uid":"1", // "name":"admin", // "mail":"wa-sysadmin@bl.uk", // "url":"http:\/\/webarchive.org.uk\/act\/user\/1", // "edit_url":"http:\/\/webarchive.org.uk\/act\/user\/1\/edit", // "last_access":"1421857188", // "last_login":"1421840203", // "created":"1355148005", // "roles":[2,3], // "status":"1", // "theme":"ukwa_basic", // "language":"en", // "feed_nid":null // } public FieldModel getField_affiliation() { return field_affiliation; } public void setField_affiliation(FieldModel field_affiliation) { this.field_affiliation = field_affiliation; } public String getUid() { return uid; } public void setUid(String uid) { this.uid = uid; } public Long getCreated() { return created; } public void setCreated(Long created) { this.created = created; } public Object getRolesAct() { return rolesAct; } public void setRolesAct(Object rolesAct) { this.rolesAct = rolesAct; } public String getTheme() { return theme; } public void setTheme(String theme) { this.theme = theme; } @JsonIgnore @Column(columnDefinition = "text") public String revision; // -- Queries public static Model.Finder<Long,User> find = new Model.Finder<>(Long.class, User.class); public User() { this.revision = ""; } public User(String name) { this.name = name; this.revision = ""; } public User(String name, String email, String password) { this.name = name; this.email = email; this.password = password; this.revision = ""; } /** * Retrieve all users. */ public static List<User> findAll() { List<User> users = find.where().orderBy("name asc").findList(); return users; } @JsonIgnore public boolean isSysAdmin() { return hasRole("sys_admin"); } @JsonIgnore public boolean isArchivist() { return hasRole("archivist"); } @JsonIgnore public boolean isExpertUser() { return hasRole("expert_user"); } @JsonIgnore public boolean isUser() { return hasRole("user"); } @JsonIgnore public boolean hasArchivistRights() { return isSysAdmin() || isArchivist(); } @JsonIgnore public boolean hasExpertUserRights() { return hasArchivistRights() || isExpertUser(); } @JsonIgnore public boolean hasUserRights() { return hasExpertUserRights() || isUser(); } @JsonIgnore public boolean canUseDDHAPT() { // If DDHAPT is enabled: if( ApplicationController.getDDHAPTStatus() ) { // Check the user's role permits DDHAPT access (maybe allow all users?): if((hasRole("sys_admin") || hasRole("archivist") || hasRole("expert_user"))) { // If user us flagged as a DDHAPT user, and is a member of the British Library (to be opened up to isLDLMember later): if( ddhaptUser && isBLMember() ) { return true; } } } return false; } @JsonIgnore public boolean canWatchTarget(Target target) { if( canUseDDHAPT() && target.authorUser != null && target.authorUser.id.equals(this.id) ) { return true; } return false; } @JsonIgnore public boolean isLDLMember() { String orgstr = getUserOrg(); if ( orgstr.equals("BL") || orgstr.equals("NLW") || orgstr.equals("NLS") || orgstr.equals("Bodleian") || orgstr.equals("CAM") || orgstr.equals("TCD") ) { return true; } return false; } @JsonIgnore public boolean isBLMember() { if ( getUserOrg().equals("BL") ) { return true; } return false; } @JsonIgnore private String getUserOrg() { String orgstr = ""; if( organisation != null ) { orgstr = organisation.field_abbreviation; } else if( affiliation != null ) { orgstr = affiliation; Logger.warn("Using user.affiliation rather than user.organisation (which is null)..."); } Logger.debug("organisation ::::::::::::::"+ orgstr); return orgstr; } /** * This method checks whether user has a role by its name. * @param roleName * @return */ public boolean hasRole(String roleName) { boolean res = false; if (StringUtils.isNotBlank(roleName)) { Role role = Role.findByName(roleName); res = this.hasRole(role); } return res; } public boolean hasRole(Role role) { return (this.roles != null && this.roles.contains(role)); } /** * This method returns all users alphabetically sorted. * @return user list */ public static List<User> findAllSorted() { List<User> users = find.where().orderBy("name asc").findList(); return users; } /** * Retrieve a User from email. */ public static User findByEmail(String email) { if( email == null ) return null; return find.where().ieq("email", email).findUnique(); } /** * Retrieve a User by name. * @param name * @return */ public static User findByName(String name) { return find.where().eq("name", name).findUnique(); } /** * Retrieve a User by URL. * @param url * @return user name */ public static User findByUrl(String url) { return find.where().eq(Const.URL, url).findUnique(); } public static User findById(Long id) { User res = find.byId(id); return res; } public static User findByWct(String url) { return find.where().eq("edit_url", url).findUnique(); } /** * Retrieve a User by UID * @param id * @return */ public static User findByUid(Long id) { return find.where().eq(Const.UID, id).findUnique(); } /** * This method returns all users related to given organisation. * @param organisation The organisation URL * @return user list */ public static List<User> findByOrganisation(String organisation) { List<User> res = new ArrayList<User>(); ExpressionList<User> ll = find.where().eq(Const.FIELD_AFFILIATION, organisation); res = ll.findList(); return res; } /** * This method returns all users related to given organisation. * @param organisation The organisation URL * @return user list */ public static List<User> findByNotEqualOrganisation(Long organisationId) { List<User> res = new ArrayList<User>(); ExpressionList<User> ll = find.where().add(Expr.or(Expr.isNull("organisation.id"), Expr.ne("organisation.id", organisationId))); res = ll.findList(); return res; } /** * This method is used for filtering by URL. * @param url * @return */ public static List<User> findFilteredByUrl(String url) { List<User> ll = new ArrayList<User>(); // Logger.debug("user findFilteredByUrl(): " + url); if (url != null && url.length() > 0 && !url.equals(Const.NONE)) { User user = find.where().eq(Const.URL, url).findUnique(); ll.add(user); } else { ll = find.all(); } return ll; } /** * Authenticate a User. */ public static User authenticate(String email, String password) { return find.where() .eq("email", email) .eq("password", password) .findUnique(); } /** * This method calculates memership period for the user. * @return */ public String calculate_membership() { String res = ""; Logger.debug("createdAt: " + createdAt + ", last_access: " + last_access + ", last_login: " + last_login); try { long timestampCreated = createdAt.getTime(); Date dateCreated = new Date(timestampCreated * 1000); long timestampLastAccess = Long.valueOf(last_access); Date dateLastAccess = new Date(timestampLastAccess * 1000); Logger.debug("date created: " + dateCreated); Logger.debug("date last access: " + dateLastAccess); DateTime dt1 = new DateTime(dateCreated); DateTime dt2 = new DateTime(dateLastAccess); Period period = new Period(dt1, dt2); PeriodFormatterBuilder formaterBuilder = new PeriodFormatterBuilder() .appendMonths().appendSuffix(" months ") .appendWeeks().appendSuffix(" weeks"); PeriodFormatter pf = formaterBuilder.toFormatter(); // Logger.debug(pf.print(period)); res = pf.print(period); } catch (Exception e) { Logger.debug("date difference calculation error: " + e); } return res; } /** * This method filters users by name and returns a list of filtered User objects. * @param name * @return */ public static List<User> filterByName(String name) { List<User> res = new ArrayList<User>(); ExpressionList<User> ll = find.where().contains(Const.EMAIL, name.toLowerCase()); res = ll.findList(); return res; } // Could really do with many_to_one relationship // public Organisation getOrganisation() { // return Organisation.findByUrl(affiliation); // } // /** * This method shows user in HTML page. * @param userUrl The link to user in Target object field 'author' * @return */ public static String showUser(String userUrl) { return User.findByUrl(userUrl).name; } /** * Return a page of User * * @param page Page to display * @param pageSize Number of Users per page * @param sortBy User property used for sorting * @param order Sort order (either or asc or desc) * @param filter Filter applied on the name column */ public static Page<User> page(int page, int pageSize, String sortBy, String order, String filter) { return find.fetch("roles").where() .icontains("name", filter) .orderBy(sortBy + " " + order) .findPagingList(pageSize) .setFetchAhead(false) .getPage(page); } public static Map<String,String> options() { LinkedHashMap<String,String> options = new LinkedHashMap<String,String>(); for(User c: User.findAll()) { options.put(c.id.toString(), c.name); } return options; } public void changePassword(String password) throws ActException { try { this.password = PasswordHash.createHash(password); // this.password = Hash.createPassword(password); this.save(); } catch (NoSuchAlgorithmException | InvalidKeySpecException e) { throw new ActException(e); } } /** * @return role (user's should only have a single role) */ @JsonIgnore public Role getRole() { if (CollectionUtils.isNotEmpty(roles)) { return roles.get(0); } return null; } @Override public int hashCode() { final int prime = 31; int result = super.hashCode(); result = prime * result + ((email == null) ? 0 : email.hashCode()); result = prime * result + ((name == null) ? 0 : name.hashCode()); result = prime * result + ((id == null) ? 0 : id.hashCode()); return result; } @Override public boolean equals(Object obj) { if (this == obj) return true; if (!super.equals(obj)) return false; if (getClass() != obj.getClass()) return false; User other = (User) obj; if (email == null) { if (other.email != null) return false; } else if (!email.equals(other.email)) return false; if (name == null) { if (other.name != null) return false; } else if (!name.equals(other.name)) return false; if (id == null) { if (other.uid != null) return false; } else if (!id.equals(other.id)) return false; return true; } @Override public String toString() { return "User [organisation=" + organisation + ", roles=" + roles + ", email=" + email + ", password=" + password + ", name=" + name + ", fieldAffiliation=" + affiliation + ", edit_url=" + edit_url + ", last_access=" + last_access + ", last_login=" + last_login + ", status=" + status + ", language=" + language + ", feed_nid=" + feed_nid + ", field_affiliation=" + field_affiliation + ", uid=" + uid + ", created=" + created + ", revision=" + revision + ", id=" + id + ", url=" + url + ", createdAt=" + createdAt + ", updatedAt=" + updatedAt + "]"; } }